using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Configuration;
using System.Runtime.Serialization;

namespace EventNotificationService
{
    

    public class ServiceModuleSection : ConfigurationSection
    {
        public const string SectionName = "ServiceModules";


        /// <summary>
        /// List of startup services that will be initialized on the host.
        /// </summary>
        [ConfigurationProperty("services", IsRequired = true)]
        public ServiceElementCollection Services
        {
            get { return (ServiceElementCollection)this["services"]; }
        }
    }

    public abstract class ParametersElement : ConfigurationElement
    {
        /// <summary>
        /// Properties we're creating on the fly as unrecognized attributes appear.
        /// </summary>
        private ConfigurationPropertyCollection dynamicProperties = new ConfigurationPropertyCollection();

        private NameValueCollection configAttributes = new NameValueCollection();

        /// <summary>
        /// Constructor for use by derived classes.
        /// </summary>
        protected ParametersElement()
        {
        }

        /// <summary>
        /// Retrieves the accumulated properties for the element, which include 
        /// the dynamically generated ones.
        /// </summary>
        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                ConfigurationPropertyCollection baseprops = base.Properties;
                foreach (ConfigurationProperty dynprop in dynamicProperties)
                {
                    baseprops.Add(dynprop);
                }
                return baseprops;
            }
        }

        /// <summary>
        /// Parameters received by the element as attributes in the configuration file.
        /// </summary>
        public NameValueCollection Parameters
        {
            get { return configAttributes; }
        }

        /// <summary>
        /// Create a new property on the fly for the attribute.
        /// </summary>
        protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
        {
            ConfigurationProperty dynprop = new ConfigurationProperty(name, typeof(string), value);
            dynamicProperties.Add(dynprop);
            this[dynprop] = value; // Add the value to values bag
            configAttributes.Add(name, value);
            return true;
        }
    }

    public class ServiceElement : ParametersElement
    {
        [ConfigurationProperty("ServiceName", IsKey = true, IsRequired = true)]
        public string ServiceName
        {
            get { return (string)this["ServiceName"]; }
            set { this["ServiceName"] = value; }
        }

        [ConfigurationProperty("EventTableName", IsKey = true, IsRequired = true)]
        public string EventTableName
        {
            get { return (string)this["EventTableName"]; }
            set { this["EventTableName"] = value; }
        }

        

        [ConfigurationProperty("SubjectLine", IsRequired = true)]
        public string SubjectLine
        {
            get { return (string)this["SubjectLine"]; }
            set { this["SubjectLine"] = value; }
        }

        [ConfigurationProperty("BodyTemplate", IsRequired = true)]
        public string BodyTemplate
        {
            get { return (string)this["BodyTemplate"]; }
            set { this["BodyTemplate"] = value; }
        }

        [ConfigurationProperty("ToAddress", IsRequired = true, DefaultValue = "EMBED")]
        public string ToAddress
        {
            get { return (string)this["ToAddress"]; }
            set { this["ToAddress"] = value; }
        }

        [ConfigurationProperty("FromAddress", IsRequired = true)]
        public string FromAddress
        {
            get { return (string)this["FromAddress"]; }
            set { this["FromAddress"] = value; }
        }

        [ConfigurationProperty("CCAddress", IsRequired = false)]
        public string CCAddress
        {
            get { return (string)this["CCAddress"]; }
            set { this["CCAddress"] = value; }
        }
        
    }

    [ConfigurationCollection(typeof(ServiceElement))]
    public class ServiceElementCollection : ConfigurationElementCollection, IEnumerable<ServiceElement>
    {
        /// <summary>
        /// Creates a new <see cref="ServiceElement"/>.
        /// </summary>
        protected override ConfigurationElement CreateNewElement()
        {
            return new ServiceElement();
        }

        /// <summary>
        /// Retrieves the key for the configuration element.
        /// </summary>
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ServiceElement)element).ServiceName;
        }

        /// <summary>
        /// Provides access to the service elements by the type of service registered.
        /// </summary>
        new public ServiceElement this[string serviceName]
        {
            get { return (ServiceElement)base.BaseGet(serviceName); }
            set
            {
                if (base.BaseGet(serviceName) != null)
                {
                    base.BaseRemove(serviceName);
                }
                base.BaseAdd(value);
            }
        }

        /// <summary>
        /// Adds a new service to the collection.
        /// </summary>
        public void Add(ServiceElement service)
        {
            base.BaseAdd(service);
        }

        /// <summary>
        /// Removes a service from the collection.
        /// </summary>
        public void Remove(string serviceName)
        {
            base.BaseRemove(serviceName);
        }

        #region IEnumerable<ServiceElement> Members

        /// <summary>
        /// Enumerates the services in the collection.
        /// </summary>
        public new IEnumerator<ServiceElement> GetEnumerator()
        {
            int count = base.Count;
            for (int i = 0; i < count; i++)
            {
                yield return (ServiceElement)base.BaseGet(i);
            }
        }

        #endregion
    }

    [Serializable]
    public class ConfigurationException : Exception
    {
        /// <summary>
        /// Initializes a new instance of the System.Exception class.
        /// </summary>
        public ConfigurationException() : base() { }

        /// <summary>
        /// Initializes a new instance of the System.Exception class with a specified error message.
        /// </summary>
        /// <param name="message">The message that describes the error.</param>
        public ConfigurationException(string message) : base(message) { }

        /// <summary>
        /// Initializes a new instance of the System.Exception class with a specified error message 
        /// and a reference to the inner exception that is the cause of this exception.
        /// </summary>
        /// <param name="message">The error message that explains the reason for the exception.</param>
        /// <param name="innerException">The exception that is the cause of the current exception, 
        /// or a null reference if no inner exception is specified.</param>
        public ConfigurationException(string message, Exception innerException) : base(message, innerException) { }

        /// <summary>
        /// Initializes a new instance of the System.Exception class with serialized data.
        /// </summary>
        /// <param name="info">The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.</param>
        /// <param name="context">The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.</param>
        protected ConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
    }
}
